package ck

import (
	"gitee.com/tietang/terrace-go/v2/example/moni/dao"
	"gitee.com/tietang/terrace-go/v2/lib/rpc"
	"github.com/sirupsen/logrus"
	"github.com/tietang/dbx"
	"strings"
)

var ckJvmDao *ClickHouseJvmDao = new(ClickHouseJvmDao)

func init() {
	//dao.Register("jvm.JvmInfo", ckJvmDao)
	handler := dao.NewHandler(ckJvmDao)
	rpc.RegisterHandler("jvm.JvmInfo", handler)
}

type ClickHouseJvmDao struct {
}

func (d *ClickHouseJvmDao) Save(metrics any) {
	met := metrics.(map[string]interface{})
	args := make([]interface{}, 0, 32)
	args = append(args, met["appName"])
	args = append(args, met["hostName"])
	args = append(args, met["port"])
	args = append(args, met["timestamp"])
	args = append(args, met["interval"])
	args = append(args, met["category"])
	instanceTimeEntity := met["instanceTimeEntity"].(map[string]interface{})
	args = append(args, instanceTimeEntity["instanceUpTime"])
	args = append(args, instanceTimeEntity["startTime"])
	cpuEntity := met["cpuEntity"].(map[string]interface{})
	args = append(args, cpuEntity["processors"])
	args = append(args, cpuEntity["systemloadAverage"])
	args = append(args, cpuEntity["processCpuTime"])
	args = append(args, cpuEntity["processCpuLoad"])
	memoryEntity := met["memoryEntity"].(map[string]interface{})
	args = append(args, memoryEntity["mem"])
	args = append(args, memoryEntity["free"])
	heapEntity := met["heapEntity"].(map[string]interface{})
	args = append(args, heapEntity["heap"])
	args = append(args, heapEntity["heapInit"])
	args = append(args, heapEntity["heapUsed"])
	args = append(args, heapEntity["heapCommitted"])
	nonHeapEntity := met["nonHeapEntity"].(map[string]interface{})
	args = append(args, nonHeapEntity["nonHeap"])
	args = append(args, nonHeapEntity["nonHeapInit"])
	args = append(args, nonHeapEntity["nonHeapUsed"])
	args = append(args, nonHeapEntity["nonHeapCommitted"])
	threadEntity := met["threadEntity"].(map[string]interface{})
	args = append(args, threadEntity["peak"])
	args = append(args, threadEntity["daemon"])
	args = append(args, threadEntity["totalStarted"])
	args = append(args, threadEntity["threads"])
	classesEntity := met["classesEntity"].(map[string]interface{})
	args = append(args, classesEntity["classes"])
	args = append(args, classesEntity["loaded"])
	args = append(args, classesEntity["unloaded"])
	metaspaceEntity := met["metaspaceEntity"].(map[string]interface{})
	args = append(args, metaspaceEntity["metaspaceCommitted"])
	args = append(args, metaspaceEntity["metaspaceUsed"])
	args = append(args, met["granularityType"])
	Clickhouse().Tx(func(run *dbx.TxRunner) error {
		_, err := run.Exec(jvmSql, args...)
		if err != nil {
			logrus.Error(err)
			return err
		}
		return nil
	})
	d.saveGc(metrics)

}

func (d *ClickHouseJvmDao) saveGc(metrics any) {
	m := metrics.(map[string]interface{})
	gcEntity := m["gcEntity"].(map[string]interface{})
	nFields := 8

	metricsList := gcEntity["gcUsageList"].([]interface{})
	args := make([]interface{}, 0, len(metricsList)*nFields)
	sql := jvmGcBaseSql + strings.Repeat(jvmGcValuesSql+",", len(metricsList)-1) + jvmGcValuesSql

	for _, metric := range metricsList {
		met := metric.(map[string]interface{})
		gcTime := met["gcTime"].(float64)
		gcCount := met["gcCount"].(float64)
		if gcTime == 0 && gcCount == 0 {
			//if structs.IsZero(met["gcTime"]) && structs.IsZero(met["gcCount"]) {
			//无GC，不保存
			continue
		}

		args = append(args, m["appName"])
		args = append(args, m["hostName"])
		args = append(args, m["timestamp"])
		args = append(args, m["interval"])
		args = append(args, met["gcName"])
		args = append(args, met["gcCount"])
		args = append(args, met["gcTime"])
		args = append(args, met["generation"])

	}
	if len(args) == 0 {
		return
	}

	Clickhouse().Tx(func(run *dbx.TxRunner) error {
		_, err := run.Exec(sql, args...)
		if err != nil {
			logrus.Error(err)
			return err
		}
		return nil
	})
}

func (d *ClickHouseJvmDao) Init() {

	Clickhouse().Tx(func(run *dbx.TxRunner) error {
		run.Exec(jvmCreateTable)
		run.Exec(jvmCreateTableGc)
		return nil
	})
}

const jvmSql = "INSERT INTO m_jvm ( appName,hostName,port,timestamp,interval,category,instanceUpTime,startTime,processors,systemloadAverage,processCpuTime,processCpuLoad,mem,free,heap,heapInit,heapUsed,heapCommitted,nonHeap,nonHeapInit,nonHeapUsed,nonHeapCommitted,peak,daemon,totalStarted,threads,classes,loaded,unloaded,metaspaceCommitted,metaspaceUsed,granularityType)  " +
	//" from input (' appName String,hostName String,port Int64,timestamp Int64,interval Int64,category String,instanceUpTime Int64,startTime Int64,processors Int64,systemloadAverage Float64,processCpuTime Int64,processCpuLoad Float64,mem Int64,free Int64,heap Int64,heapInit Int64,heapUsed Int64,heapCommitted Int64,nonHeap Int64,nonHeapInit Int64,nonHeapUsed Int64,nonHeapCommitted Int64,peak Int64,daemon Int64,totalStarted Int64,threads Int64,classes Int64,loaded Int64,unloaded Int64,metaspaceCommitted Int64,metaspaceUsed Int64,granularityType Int64')"
	" values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"

const jvmGcBaseSql = "INSERT INTO m_gc (appName,hostName,timestamp,interval,gcName,gcCount,gcTime,generation) " +
	//" from input ('  appName String, hostName String, timestamp UInt64, interval Int64, gcName String,gcCount Int64,gcTime Int64,generation Int64') "
	" values"
const jvmGcValuesSql = "(?,?,?,?,?,?,?,?)"
const jvmCreateTable = "create table if not exists m_jvm " +
	" (" +
	" appName String," +
	" hostName String," +
	" port Int64," +
	" timestamp UInt64," +
	" interval Int64," +
	" category String," +
	" instanceUpTime Int64," +
	" startTime Int64," +
	" processors Int64," +
	" systemloadAverage Float64," +
	" processCpuTime Int64," +
	" processCpuLoad Float64," +
	" mem Int64," +
	" free Int64," +
	" heap Int64," +
	" heapInit Int64," +
	" heapUsed Int64," +
	" heapCommitted Int64," +
	" nonHeap Int64," +
	" nonHeapInit Int64," +
	" nonHeapUsed Int64," +
	" nonHeapCommitted Int64," +
	" peak Int64," +
	" daemon Int64," +
	" totalStarted Int64," +
	" threads Int64," +
	" classes Int64," +
	" loaded Int64," +
	" unloaded Int64," +
	" metaspaceCommitted Int64," +
	" metaspaceUsed Int64," +
	" granularityType Int64," +
	" d Date materialized toDate(round(timestamp / 1000))," +
	" dt DateTime materialized toDateTime(round(timestamp / 1000))" +
	" ) " +
	" engine = MergeTree PARTITION BY (toYYYYMM(dt), appName)\n" +
	"        ORDER BY dt\n" +
	"        SETTINGS index_granularity = 8192;"

const jvmCreateTableGc = "create table if not exists m_gc" +
	" (" +
	" appName String," +
	" hostName String," +
	" timestamp UInt64," +
	" interval Int64," +
	" gcName String," +
	" gcCount Int64," +
	" gcTime Int64," +
	" generation Int64," +
	" d Date materialized toDate(round(timestamp / 1000))," +
	" dt DateTime materialized toDateTime(round(timestamp / 1000))" +
	" ) " +
	" engine = MergeTree PARTITION BY (toYYYYMM(dt), appName)\n" +
	"        ORDER BY dt\n" +
	"        SETTINGS index_granularity = 8192;"
